package com.meida.module.arc.iam.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.meida.common.base.handler.AdminLoginHandler;
import com.meida.common.base.module.LoginParams;
import com.meida.common.base.utils.FlymeUtils;
import com.meida.common.configuration.OpenCommonProperties;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.oauth2.OpenOAuth2ClientDetails;
import com.meida.common.oauth2.OpenOAuth2ClientProperties;
import com.meida.common.security.OpenHelper;
import com.meida.common.security.http.OpenRestTemplate;
import com.meida.common.utils.SpringContextHolder;
import com.meida.module.arc.iam.bean.LoginUser;
import com.meida.module.arc.iam.config.OauthConfigurationProperties;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;


@RestController
@RequestMapping("/act/common/")
public class OauthController {

    @Autowired
    OauthConfigurationProperties oauthConfigurationProperties;

    @Autowired
    OpenCommonProperties openCommonProperties;

    @Autowired
    OpenRestTemplate openRestTemplate;


    @Autowired
    OpenOAuth2ClientProperties clientProperties;
    @Autowired
    AuthorizationServerEndpointsConfiguration endpoints;

    @RequestMapping(value = "/tokenLogin", method = {RequestMethod.POST, RequestMethod.GET})
    public LoginUser tokenLogin() throws IOException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String code = request.getParameter("code");
        String accessToken = "";
        if (StrUtil.isNotEmpty(code)) {
            System.out.println("code:" + code);
            accessToken = getAccessToken(code);
        } else {
            accessToken = request.getParameter("access_token");
        }
        System.out.println("accessToken数据：" + accessToken);

        LoginUser loginUser = getUsernameByToken(accessToken);
        return loginUser;
    }

    /**
     * 根据code获取access_token
     *
     * @param code
     * @return
     * @throws IOException
     */
    private String getAccessToken(String code) throws IOException {
        String url = oauthConfigurationProperties.getTokenUrl() + "?client_id=" + oauthConfigurationProperties.getClientId() +
                "&client_secret=" + oauthConfigurationProperties.getClientSecret() + "&grant_type=authorization_code" +
                "&redirect_uri=" + oauthConfigurationProperties.getRedirectUri() + "&code=" + code;

        System.err.println(url);
        HttpRequestBase httpRequest = new HttpGet(url);
        String httpType = "http";
        if (url.startsWith("https")) {
            httpType = "https";
        }

        HttpClient httpClient = getHttpClient(httpType);

        HttpResponse httpResponse = httpClient.execute(httpRequest);

        if (httpResponse.getStatusLine().getStatusCode() == 200) {

            HttpEntity repentity = httpResponse.getEntity();//调用getEntity()方法获取到一个HttpEntity实例
            String resp = EntityUtils.toString(repentity, "utf-8");//用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了

            Map<String, String> maps = (Map<String, String>) JSON.parse(resp);
            if (maps.containsKey("access_token")) {
                return maps.get("access_token");
            }
            return null;
        } else {
            System.err.println(httpResponse.getStatusLine().getStatusCode());

            return null;
        }
    }

    /**
     * 根据access_token获取用户
     *
     * @param accessToken
     * @return
     * @throws IOException
     */
    private LoginUser getUsernameByToken(String accessToken) throws IOException {

        String url = oauthConfigurationProperties.getProfileUrl() + "?access_token=" + accessToken;

        HttpRequestBase httpRequest = new HttpGet(url);

        String httpType = "http";
        if (url.startsWith("https")) {
            httpType = "https";
        }

        HttpClient httpClient = getHttpClient(httpType);

        HttpResponse httpResponse = httpClient.execute(httpRequest);
        if (httpResponse.getStatusLine().getStatusCode() == 200) {

            HttpEntity repentity = httpResponse.getEntity();//调用getEntity()方法获取到一个HttpEntity实例
            String resp = EntityUtils.toString(repentity, "utf-8");//用EntityUtils.toString()这个静态方法将HttpEntity转换成字符串,防止服务器返回的数据带有中文,所以在转换的时候将字符集指定成utf-8就可以了
            System.out.println("接收数据：" + resp);
            LoginUser loginUser = JSON.parseObject(resp, LoginUser.class);
            return loginUser;
        } else {
            return null;
        }
    }

    private HttpClient getHttpClient(String httpType) {
        HttpClient httpClient = null;

        if ("https".equals(httpType)) {
            try {
                httpClient = new SSLHttpClient();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            httpClient = new DefaultHttpClient();
        }
        return httpClient;
    }

    class SSLHttpClient extends DefaultHttpClient {
        public SSLHttpClient() throws Exception {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain,
                                               String authType) throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            ctx.init(null, new TrustManager[]{tm}, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx,
                    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = this.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", 443, ssf));
        }
    }


    @RequestMapping(value = "/ssoLogin", method = {RequestMethod.POST, RequestMethod.GET})
    public Object ssoLogin(@RequestHeader HttpHeaders headers) throws Exception {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String code = request.getParameter("code");
        String userName = request.getParameter("username");
        String accessToken = request.getParameter("access_token");
        if (StrUtil.isNotEmpty(code)) {
            System.out.println("code:" + code);
            accessToken = getAccessToken(code);
        } else {
            accessToken = request.getParameter("access_token");
        }
        if (StrUtil.isNotEmpty(accessToken)) {
            LoginUser loginUser = getUsernameByToken(accessToken);
            userName = loginUser.getAccount();
        }
        LoginParams loginParams = new LoginParams();
        loginParams.setAccountType("NO_PASSWORD");
        loginParams.setUsername(userName);

        return ResultBody.ok(getToken(loginParams, ""));
    }

    /**
     * 第三方登录获取token
     *
     * @return
     */
    public Map<String, Object> getToken(LoginParams loginParams, String type) throws Exception {
        AdminLoginHandler loginHandler = SpringContextHolder.getHandler(loginParams.getHandlerName(), AdminLoginHandler.class);
        Boolean hanHandler = false;
        if (FlymeUtils.isNotEmpty(loginHandler)) {
            hanHandler = true;
            //执行校验
        }
        OpenOAuth2ClientDetails clientDetails = (OpenOAuth2ClientDetails) this.clientProperties.getOauth2().get("admin");
        // 使用oauth2密码模式登录.
        Map<String, String> postParameters = new HashMap<>();
        postParameters.put("username", loginParams.getUsername());
        postParameters.put("password", loginParams.getPassword());
        postParameters.put("client_id", clientDetails.getClientId());
        postParameters.put("client_secret", clientDetails.getClientSecret());
        postParameters.put("grant_type", "password");
        // 添加参数区分,第三方登录
        postParameters.put("login_type", type);
        OAuth2AccessToken oAuth2AccessToken = OpenHelper.createAccessToken(endpoints, postParameters);
        Map<String, Object> map = new HashMap<>(16);
        if (FlymeUtils.isNotEmpty(oAuth2AccessToken)) {
            map.put("access_token", "Bearer " + oAuth2AccessToken.getValue());
            map.put("token_type", oAuth2AccessToken.getTokenType());
            map.put("expires_in", oAuth2AccessToken.getExpiresIn());
            map.put("refresh_token", Optional.ofNullable(oAuth2AccessToken.getRefreshToken()).orElse(() -> "").getValue());
            map.putAll(oAuth2AccessToken.getAdditionalInformation());
            if (hanHandler) {
                loginHandler.afterLogin(map, loginParams);
            }
        }
        return map;
    }
}
